/*
    Author: Christopher Diggins
    License: MIT Licence 1.0
    Website: http://www.cdiggins.com
*/

#ifndef LUA_MACHINE_HPP
#define LUA_MACHINE_HPP

struct LuaMachine
{
	String S;
	lua_State* L;

	static int Panic(lua_State* state) {
		Error("lua panicked");
		return 0;
	}

	LuaMachine() {
		Start();
	}

	~LuaMachine() {
		Stop();
	}

	void Start() {
		L = lua_open();
		luaL_openlibs(L);
		lua_atpanic(L, Panic);
	}

	void Stop() {
		lua_close(L);
	}

	void Reset() {
		Stop();
		Start();
		S = "";
	}

	void NewTable() {
		lua_newtable(L);
	}

	void SetGlobal(ConstString name) {
		lua_setglobal(L, name.c_str());
	}

	void GetGlobal(ConstString name) {
		lua_getglobal(L, name.c_str());
	}

	bool IsNil() {
		return lua_isnil(L, -1);
	}

	bool GlobalExists(ConstString s) {
		Error("Does not work as expected");
		/*
		GetGlobal(s);
		bool r = IsNil();
		Pop();
		return r;
		*/
		return false;
	}

	void Pop(int n = 1) {
		lua_pop(L, n);
	}

	void LoadString(ConstString s) {
		S += s;
	}

	void Clear() {
		S = "";
	}

	void Eval(const char* x) {
		LoadString(x);
		Eval();
	}

	void Eval() {
		int n = luaL_loadstring(L, S.c_str());
		
		if (n != 0) {
			if (n == LUA_ERRSYNTAX)
				Error("Lua syntax error");
			else if (n == LUA_ERRMEM)
				Error("Lua memory error");
			else 
				Error("unrecognized Lua error while loading file");
		}
					
		//n = lua_pcall(L, 0, LUA_MULTRET, 0);
		n = lua_pcall(L, 0, 0, 0);

		if (n != 0) {
			if (n == LUA_ERRRUN) {
				if (lua_isstring(L, 1)) {
					const char* msg = lua_tostring(L, 1);
					Error(msg);
				}				
				
				Error("Lua runtime error");
			}
			else if (n == LUA_ERRMEM)
				Error("Lua memory error");
			else if (n == LUA_ERRERR)
				Error("Lua error during error handling");
			else 
				Error("Unrecognized Lua error");
		}
	}
	
	void operator+=(ConstString s) {
		LoadString(s);
	}

	String& GetText() {
		return S;
	}
	
	String& GetPrettyText() {
		Prettify();
		return GetText();
	}

	bool WordMatches(ConstString s, size_t i, ConstString word) {
		if (s.size() < i + word.size() + 1)
			return false;
		for (size_t j = 0; j < word.size(); ++j) 
			if (s[i++] != s[j])
				return false;
		if (IsAlphaNumeric(s[i])) 
			return false;
		return true;
	}

	void Prettify() {
		String r;
		int nIndent = 0;
		for (size_t i = 0; i < S.size(); ++i) 
		{
			r.push_back(S[i]);
			switch (S[i])
			{
			case '{':
				nIndent++;
				break;
			case '}':
				nIndent--;
				break;
			case '\n':
				for (int j=0; j < nIndent * 2; ++j) 
					r.push_back(' ');
				break;
			default:
				break;
			}
		};
		S = r;
	}

	lua_State* GetState() {
		return L;
	}
};

#endif
